#pragma once
#include "cJSON.h"
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map>
#include <ostream>
#include <sstream>
#include <string>
#include <vector>
#include <atomic>
using namespace std;
#include <unistd.h>
#include "device.h"
#include "m2_proto.h"

struct outair_dev;

enum {
	NOTHING = 0,
	REGISTER_READY,
	TOPO_ADD_READY,
	LOGIN_READY,
	DISABLED,
};

struct inair_status {
	string addr;
	int runing;
	int mode;
	int wind;
	string temp;
	string room_temp;
	int err_code;

	int online;
	int aliyun_init_status;
	int aliyun_status;

	bool operator==(const inair_status &st)
	{
		return st.runing == runing && st.temp == temp && st.mode == mode && st.wind == wind && st.room_temp == room_temp && st.err_code == err_code;
	}

	inair_status &operator=(const inair_status &st)
	{
		runing = st.runing;
		temp = st.temp;
		mode = st.mode;
		wind = st.wind;
		room_temp = st.room_temp;
		err_code = st.err_code;
		return *this;
	}

	friend ostream &operator<<(ostream &os, const inair_status &st)
	{
		os << "{"
		   << "\"Addr\":\"" << st.addr << "\","
		   << "\"Online\":" << st.online << ","
		   << "\"Aliyun_status\":" << st.aliyun_status << ","
		   << "\"Switch\":" << st.runing << ","
		   << "\"WorkMode\":" << st.mode << ","
		   << "\"WindSpeed\":" << st.wind << ","
		   << "\"CurrentTemperature\":" << (st.room_temp == "" ? "0" : st.room_temp) << ","
		   << "\"TargetTemperature\":" << (st.temp == "" ? "0" : st.temp) << ","
		   << "\"ErrorCode\":" << st.err_code << "}";
		return os;
	}

	friend ostream &operator<<(ostream &os, const inair_status *st)
	{
		os << "{"
		   << "\"Switch\":" << st->runing << ","
		   << "\"WorkMode\":" << st->mode << ","
		   << "\"WindSpeed\":" << st->wind << ","
		   << "\"CurrentTemperature\":" << (st->room_temp == "" ? "0" : st->room_temp) << ","
		   << "\"TargetTemperature\":" << (st->temp == "" ? "0" : st->temp) << ","
		   << "\"ErrorCode\":" << st->err_code << "}";
		return os;
	}
};

struct inair_dev {
	string id;
	string addr;
	string secret;
	string name;
	string area;
	string scenario;

	outair_dev *outair;
	friend ostream &operator<<(ostream &os, const inair_dev &dev)
	{
		os << "{"
		   << "\"Id\":\"" << dev.id << "\","
		   << "\"Addr\":\"" << dev.addr << "\","
		   << "\"Secret\":\"\","
		   << "\"Name\":\"" << dev.name << "\","
		   << "\"Area\":\"" << dev.area << "\","
		   << "\"Scenario\":\"" << dev.scenario << "\""
		   << "}";
		return os;
	}

	inair_dev() = default;
	inair_dev(const cJSON *inair, outair_dev *outair)
	{
		cJSON *id = cJSON_GetObjectItemCaseSensitive(inair, "Id");
		cJSON *name = cJSON_GetObjectItemCaseSensitive(inair, "Name");
		cJSON *addr = cJSON_GetObjectItemCaseSensitive(inair, "Addr");
		cJSON *area = cJSON_GetObjectItemCaseSensitive(inair, "Area");
		cJSON *scenario = cJSON_GetObjectItemCaseSensitive(inair, "Scenario");
		cJSON *secret = cJSON_GetObjectItemCaseSensitive(inair, "Secret");
		this->id = id->valuestring;
		this->name = name->valuestring;
		this->addr = addr->valuestring;
		this->area = area->valuestring;
		this->scenario = scenario->valuestring;
		this->secret = secret->valuestring;
		this->outair = outair;
	}
};

struct outair_dev {
	string id;
	string name;
	string addr;
	map<string, inair_dev *> inair;
	dev_cdt *cdt;

	outair_dev() = default;
	outair_dev(const cJSON *outair, dev_cdt *cdt)
	{
		cJSON *name = cJSON_GetObjectItemCaseSensitive(outair, "Name");
		cJSON *addr = cJSON_GetObjectItemCaseSensitive(outair, "Addr");
		cJSON *id = cJSON_GetObjectItemCaseSensitive(outair, "Id");
		this->name = name->valuestring;
		this->addr = addr->valuestring;
		this->id = id->valuestring;
		this->cdt = cdt;
	}
};

struct M2_air_cdt : public dev_cdt {
	int aly_status = 0;
	atomic_uint32_t &alyid;
	map<string, pair<uint32_t, int>> mp_post_id;

	string id;
	string name;
	string ctrl_dev;
	map<string, outair_dev *> outair;

	map<string, inair_status> mp_dev_status;
	map<string, inair_dev *> mp_addr_dev;
	map<string, map<string, inair_dev *>> mp_area_dev;
	map<string, map<string, inair_dev *>> mp_saio_dev;
	uint32_t scan_cnt = 0;
	m2_proto proto;

	cJSON *to_json()
	{
		cJSON *air = cJSON_CreateObject();
		cJSON_AddStringToObject(air, "Id", id.c_str());
		cJSON_AddStringToObject(air, "Name", name.c_str());
		cJSON_AddStringToObject(air, "Ctrl_dev", ctrl_dev.c_str());
		cJSON *outair_list = cJSON_AddArrayToObject(air, "Outdoor_llst");
		for (auto oit = outair.begin(); oit != outair.end(); oit++) {
			cJSON *outair = cJSON_CreateObject();
			cJSON_AddStringToObject(outair, "Id", (oit->second)->id.c_str());
			cJSON_AddStringToObject(outair, "Name", (oit->second)->name.c_str());
			cJSON_AddStringToObject(outair, "Addr", (oit->second)->addr.c_str());
			cJSON *inair_list = cJSON_AddArrayToObject(outair, "Indoor_list");
			for (auto iit = (oit->second)->inair.begin(); iit != (oit->second)->inair.end(); iit++) {
				cJSON *inair = cJSON_CreateObject();
				cJSON_AddStringToObject(inair, "Id", (iit->second)->id.c_str());
				cJSON_AddStringToObject(inair, "Name", (iit->second)->name.c_str());
				cJSON_AddStringToObject(inair, "Addr", (iit->second)->addr.c_str());
				cJSON_AddStringToObject(inair, "Area", (iit->second)->area.c_str());
				cJSON_AddStringToObject(inair, "Scenario", (iit->second)->scenario.c_str());
				cJSON_AddStringToObject(inair, "Secret", (iit->second)->secret.c_str());
				cJSON_AddItemToArray(inair_list, inair);
			}
			cJSON_AddItemToArray(outair_list, outair);
		}
		return air;
	}

	M2_air_cdt(const cJSON *air, atomic_uint32_t &yid) : alyid(yid)
	{
		cJSON *id = cJSON_GetObjectItemCaseSensitive(air, "Id");
		cJSON *name = cJSON_GetObjectItemCaseSensitive(air, "Name");
		cJSON *ctrl_dev = cJSON_GetObjectItemCaseSensitive(air, "Ctrl_dev");
		this->id = id->valuestring;
		this->name = name->valuestring;
		this->ctrl_dev = ctrl_dev->valuestring;

		cJSON *outairs = cJSON_GetObjectItemCaseSensitive(air, "Outdoor_llst");
		cJSON *outair = nullptr;
		cJSON_ArrayForEach(outair, outairs)
		{
			outair_dev *odev = new outair_dev(outair, this);
			cJSON *inairs = cJSON_GetObjectItemCaseSensitive(outair, "Indoor_list");
			cJSON *inair = nullptr;
			cJSON_ArrayForEach(inair, inairs)
			{
				inair_dev *idev = new inair_dev(inair, odev);
				odev->inair[idev->addr] = idev;

				mp_dev_status[idev->addr] = inair_status{};
				mp_dev_status[idev->addr].addr = idev->addr;
				mp_dev_status[idev->addr].aliyun_init_status = (idev->secret == "") ? NOTHING : REGISTER_READY;
				mp_dev_status[idev->addr].aliyun_status = mp_dev_status[idev->addr].aliyun_init_status;
				mp_addr_dev[idev->addr] = idev;
				mp_area_dev[idev->area][idev->addr] = idev;
				mp_saio_dev[idev->scenario][idev->addr] = idev;
			}
			this->outair[odev->addr] = odev;
		}
	}

	void start()
	{
		if (this->ctrl_dev == "NONE") {
			return;
		}
		proto.start(this->ctrl_dev, this->name, this->id);
	}

	int analysis_scan_offline_result(const cJSON *params)
	{
		cJSON *air_name = cJSON_GetObjectItemCaseSensitive(params, "Air_name");
		if (!(cJSON_IsString(air_name) && air_name->valuestring != NULL) || air_name->valuestring != name) {
			return -1;
		}
		cJSON *devs = cJSON_GetObjectItemCaseSensitive(params, "Devs");

		cJSON *dev = NULL, *obj = NULL;
		cJSON *json = cJSON_CreateObject();
		cJSON_AddStringToObject(json, "Active", "SUBDEV_LOGOUT");
		cJSON *dev_array = cJSON_AddArrayToObject(json, "Params");
		int cnt = 0;
		cJSON_ArrayForEach(dev, devs)
		{
			mp_dev_status[dev->valuestring].online = 0;
			mp_dev_status[dev->valuestring].aliyun_status = mp_dev_status[dev->valuestring].aliyun_init_status;
			obj = cJSON_CreateObject();
			cJSON_AddStringToObject(obj, "Name", dev->valuestring);
			cJSON_AddStringToObject(obj, "Secret", mp_addr_dev[dev->valuestring]->secret.c_str());
			cJSON_AddItemToArray(dev_array, obj);
			cnt++;
		}
		if (cnt) {
			char *ptr = cJSON_Print(json);
			send_msg(CLOUD_QUEUE, ptr, strlen(ptr));
			free(ptr);
		}
		cJSON_Delete(json);
		return 0;
	}

	int analysis_scan_online_result(const cJSON *params)
	{
		cJSON *air_name = cJSON_GetObjectItemCaseSensitive(params, "Air_name");
		if (!(cJSON_IsString(air_name) && air_name->valuestring != NULL) || air_name->valuestring != name) {
			return -1;
		}
		cJSON *devs = cJSON_GetObjectItemCaseSensitive(params, "Devs");

		int save = 0;
		cJSON *dev = NULL;
		cJSON_ArrayForEach(dev, devs)
		{
			cJSON *oaddr = cJSON_GetObjectItemCaseSensitive(dev, "Outaddr");
			string outaddr = cJSON_GetObjectItemCaseSensitive(dev, "OutDevName")->valuestring;
			outair_dev *odev = outair[outaddr];
			if (odev == NULL) {
				odev = new outair_dev;
				odev->id = to_string(oaddr->valueint);
				odev->name = outaddr;
				odev->addr = outaddr;
				odev->cdt = this;
				outair[outaddr] = odev;
				save = 1;
			}

			cJSON *iaddr = cJSON_GetObjectItemCaseSensitive(dev, "Inaddr");
			string inaddr = cJSON_GetObjectItemCaseSensitive(dev, "InDevName")->valuestring;

			inair_status istatus;
			istatus.addr = inaddr;
			istatus.mode = cJSON_GetObjectItemCaseSensitive(dev, "WorkMode")->valueint;
			istatus.room_temp = to_string(cJSON_GetObjectItemCaseSensitive(dev, "CurrentTemperature")->valuedouble);
			istatus.temp = to_string(cJSON_GetObjectItemCaseSensitive(dev, "TargetTemperature")->valuedouble);
			istatus.wind = cJSON_GetObjectItemCaseSensitive(dev, "WindSpeed")->valueint;
			istatus.runing = cJSON_GetObjectItemCaseSensitive(dev, "Switch")->valueint;
			istatus.err_code = cJSON_GetObjectItemCaseSensitive(dev, "ErrorCode")->valueint;
			istatus.online = 1;
			istatus.aliyun_status = NOTHING;
			istatus.aliyun_init_status = NOTHING;

			inair_dev *idev = mp_addr_dev[inaddr];
			if (idev == NULL) {
				idev = new inair_dev;
				idev->id = to_string(iaddr->valueint);
				idev->addr = inaddr;
				idev->name = inaddr;
				idev->outair = odev;
				odev->inair[inaddr] = idev;
				mp_addr_dev[inaddr] = idev;
				mp_dev_status[inaddr] = istatus;
				mp_dev_status[inaddr].online = 1;
				mp_dev_status[inaddr].aliyun_status = NOTHING;
				mp_dev_status[inaddr].aliyun_init_status = NOTHING;
				save = 1;
			} else {
				if (mp_dev_status[inaddr].aliyun_status == LOGIN_READY) {
					if (!(istatus == mp_dev_status[inaddr])) {
						mp_dev_status[inaddr] = istatus;
						report_property_byaddr(inaddr.c_str());
					}
				} else {
					mp_dev_status[inaddr] = istatus;
				}
				mp_dev_status[inaddr].online = 1;
			}
		}
		if (scan_cnt) {
			all_subdev_online();
		}
		scan_cnt++;
		check_post_replay();
		return save;
	}

	int set_dev_area(const cJSON *params)
	{
		cJSON *addr = cJSON_GetObjectItemCaseSensitive(params, "Addr");
		cJSON *area = cJSON_GetObjectItemCaseSensitive(params, "Area");
		if (!cJSON_IsString(addr) || !cJSON_IsString(area) || addr->valuestring == NULL || area->valuestring == NULL) {
			return 0;
		}

		auto it = mp_addr_dev.find(addr->valuestring);
		if (it != mp_addr_dev.end()) {
			if (it->second->area == area->valuestring) {
				return 0;
			}

			auto area_it = mp_area_dev.find((it->second)->area);
			if (area_it != mp_area_dev.end()) {
				area_it->second.erase(addr->valuestring);
				//mp_area_dev[it->second->area].erase(addr->valuestring);
			}
			it->second->area = area->valuestring;
			mp_area_dev[area->valuestring][addr->valuestring] = it->second;
			return 1;
		}
		return -1;
	}

	int set_dev_scenario(const cJSON *params)
	{
		cJSON *addr = cJSON_GetObjectItemCaseSensitive(params, "Addr");
		cJSON *saio = cJSON_GetObjectItemCaseSensitive(params, "Scenario");
		if (!cJSON_IsString(addr) || !cJSON_IsString(saio) || addr->valuestring == NULL || saio->valuestring == NULL) {
			return 0;
		}

		auto it = mp_addr_dev.find(addr->valuestring);
		if (it != mp_addr_dev.end()) {
			if (it->second->scenario == saio->valuestring) {
				return 0;
			}

			auto saio_it = mp_saio_dev.find(it->second->scenario);
			if (saio_it != mp_saio_dev.end()) {
				saio_it->second.erase(addr->valuestring);
			}
			it->second->scenario = saio->valuestring;
			mp_saio_dev[saio->valuestring][addr->valuestring] = it->second;
			return 1;
		}

		return -1;
	}

	int get_all_devstatus(cJSON *devs)
	{
		for (auto it = mp_dev_status.begin(); it != mp_dev_status.end(); it++) {
			ostringstream os;
			os << it->second;
			cJSON *status = cJSON_Parse(os.str().c_str());
			cJSON_AddItemToArray(devs, status);
		}
		return 0;
	}

	int get_status_byaddr(const char *addr, cJSON **status)
	{
		if (mp_dev_status.find(addr) != mp_dev_status.end()) {
			ostringstream os;
			os << mp_dev_status[addr];
			*status = cJSON_Parse(os.str().c_str());
			return os.str().size();
		}
		return 0;
	}

	int get_status_byarea(const char *area, cJSON *status)
	{
		if (mp_area_dev.find(area) == mp_area_dev.end()) {
			return -1;
		}

		for (auto it = mp_area_dev[area].begin(); it != mp_area_dev[area].end(); it++) {
			ostringstream os;
			os << mp_dev_status[it->second->addr];
			cJSON *st = cJSON_Parse(os.str().c_str());
			cJSON_AddItemToArray(status, st);
		}
		return 0;
	}

	int get_status_bysaio(const char *saio, cJSON *status)
	{
		if (mp_saio_dev.find(saio) == mp_saio_dev.end()) {
			return -1;
		}

		for (auto it = mp_saio_dev[saio].begin(); it != mp_saio_dev[saio].end(); it++) {
			ostringstream os;
			os << mp_dev_status[it->second->addr];
			cJSON *st = cJSON_Parse(os.str().c_str());
			cJSON_AddItemToArray(status, st);
		}
		return 0;
	}

	int ctrl_dev_byaddr(const char *addr, const cJSON *params)
	{
		inair_dev *idev = mp_addr_dev[addr];
		if (idev == NULL) {
			return -1;
		}
		cJSON *json = cJSON_CreateObject();
		cJSON_AddStringToObject(json, "Active", "CTRL_ONE_DEV");

		cJSON *dev = cJSON_Duplicate(params, 1);
		cJSON_AddStringToObject(dev, "Iaddr", idev->id.c_str());
		cJSON_AddStringToObject(dev, "Oaddr", idev->outair->id.c_str());
		cJSON_AddItemToObject(json, "Devs", dev);

		char *ptr = cJSON_Print(json);
		string air_queue = "/" + this->name + "_" + id;
		send_msg(air_queue.c_str(), ptr, strlen(ptr));
		free(ptr);
		cJSON_Delete(json);
		return 0;
	}

	template <typename T> int ctrl_dev_op(T &t, const char *key, const cJSON *params)
	{
		cJSON *json = cJSON_CreateObject();
		cJSON_AddStringToObject(json, "Active", "CTRL_MULTI_DEVS");
		cJSON *devs = cJSON_AddObjectToObject(json, "Devs");
		cJSON *addrs = cJSON_AddArrayToObject(devs, "Addrs");
		int cnt = 0;
		for (auto it = t[key].begin(); it != t[key].end(); it++) {
			inair_dev *idev = it->second;
			if (mp_dev_status[idev->addr].online == 0) {
				continue;
			}
			cJSON *addr = cJSON_CreateObject();
			cJSON_AddStringToObject(addr, "Iaddr", idev->id.c_str());
			cJSON_AddStringToObject(addr, "Oaddr", idev->outair->id.c_str());
			cJSON_AddItemToArray(addrs, addr);
			cnt++;
		}
		if (cnt) {
			cJSON *property = cJSON_Duplicate(params, 1);
			cJSON_AddItemToObject(devs, "Property", property);
			string air_queue = "/" + name + "_" + id;
			char *ptr = cJSON_Print(json);
			send_msg(air_queue.c_str(), ptr, strlen(ptr));
			free(ptr);
		}
		cJSON_Delete(json);
		return 0;
	}

	int set_property_bysaio(const char *saio, const cJSON *params)
	{
		if (mp_saio_dev.find(saio) == mp_saio_dev.end()) {
			return -1;
		}

		ctrl_dev_op(mp_saio_dev, saio, params);
		return 0;
	}

	int set_property_byarea(const char *area, const cJSON *params)
	{
		if (mp_area_dev.find(area) == mp_area_dev.end()) {
			return -1;
		}

		ctrl_dev_op(mp_area_dev, area, params);
		return 0;
	}

	int report_property_byaddr(const char *addr)
	{
		cJSON *json = cJSON_CreateObject();
		cJSON_AddStringToObject(json, "Active", "SET_SUBDEV_PROPERTY");

		cJSON *subpm = cJSON_AddObjectToObject(json, "Params");
		cJSON_AddStringToObject(subpm, "Name", addr);
		cJSON_AddStringToObject(subpm, "ID", to_string(++alyid).c_str());
		mp_post_id[addr] = { alyid, 0 };

		ostringstream os;
		os << &mp_dev_status[addr];
		cJSON *property = cJSON_Parse(os.str().c_str());
		cJSON_AddItemToObject(subpm, "Property", property);

		char *ptr = cJSON_Print(json);
		send_msg(CLOUD_QUEUE, ptr, strlen(ptr));
		free(ptr);
		cJSON_Delete(json);
		return 0;
	}

	int set_property_byaddr(const char *addr, const cJSON *params)
	{
		if (mp_dev_status.find(addr) == mp_dev_status.end()) {
			return -1;
		}

		return ctrl_dev_byaddr(addr, params);
		return 1;
	}

	int get_all_dev(cJSON *devs)
	{
		for (auto it = mp_addr_dev.begin(); it != mp_addr_dev.end(); it++) {
			ostringstream os;
			os << *it->second;
			cJSON *dev = cJSON_Parse(os.str().c_str());
			cJSON_AddItemToArray(devs, dev);
		}
		return 0;
	}

	int get_dev_byaddr(const char *addr, cJSON **dev)
	{
		if (mp_addr_dev.find(addr) == mp_addr_dev.end()) {
			return -1;
		}
		ostringstream os;
		os << *mp_addr_dev[addr];
		*dev = cJSON_Parse(os.str().c_str());
		return os.str().size();
	}

	int get_dev_byarea(const char *area, cJSON *devs)
	{
		for (auto it = mp_area_dev[area].begin(); it != mp_area_dev[area].end(); it++) {
			ostringstream os;
			os << *it->second;
			cJSON *dev = cJSON_Parse(os.str().c_str());
			cJSON_AddItemToArray(devs, dev);
		}
		return 0;
	}

	int get_dev_bysaio(const char *saio, cJSON *devs)
	{
		if (mp_saio_dev.find(saio) == mp_saio_dev.end()) {
			return -1;
		}
		for (auto it = mp_saio_dev[saio].begin(); it != mp_saio_dev[saio].end(); it++) {
			ostringstream os;
			os << *it->second;
			cJSON *dev = cJSON_Parse(os.str().c_str());
			cJSON_AddItemToArray(devs, dev);
		}
		return 0;
	}

	int all_subdev_online()
	{
		if (aly_status == 0) {
			return 0;
		}

		cJSON *login_obj = cJSON_CreateObject();
		cJSON_AddStringToObject(login_obj, "Active", "SUBDEV_LOGIN");
		cJSON *login_array = cJSON_AddArrayToObject(login_obj, "Params");

		cJSON *addtop_obj = cJSON_CreateObject();
		cJSON_AddStringToObject(addtop_obj, "Active", "SUBDEV_ADD_TOPO");
		cJSON *addtop_array = cJSON_AddArrayToObject(addtop_obj, "Params");

		cJSON *register_obj = cJSON_CreateObject();
		cJSON_AddStringToObject(register_obj, "Active", "SUBDEV_REGISTER");
		cJSON *register_array = cJSON_AddArrayToObject(register_obj, "Params");

		cJSON *json = NULL;
		int login_cnt = 0, addtop_cnt = 0, register_cnt = 0;
		for (auto it = mp_addr_dev.begin(); it != mp_addr_dev.end(); it++) {
			if (!mp_dev_status[it->second->addr].online) {
				continue;
			}
			switch (mp_dev_status[it->second->addr].aliyun_status) {
			case REGISTER_READY: {
				addtop_cnt++;
				json = addtop_array;
			} break;
			case TOPO_ADD_READY: {
				login_cnt++;
				json = login_array;
			} break;
			case LOGIN_READY: {
				json = NULL;
			} break;
			case NOTHING: {
				register_cnt++;
				json = register_array;
			} break;
			default: {
				json = NULL;
			} break;
			}

			if (json == NULL) {
				continue;
			}

			cJSON *dev = cJSON_CreateObject();
			cJSON_AddStringToObject(dev, "Name", it->second->addr.c_str());
			cJSON_AddStringToObject(dev, "Secret", it->second->secret.c_str());
			cJSON_AddItemToArray(json, dev);
		}
		if (login_cnt) {
			char *ptr = cJSON_Print(login_obj);
			send_msg(CLOUD_QUEUE, ptr, strlen(ptr));
			free(ptr);
		}
		if (addtop_cnt) {
			char *ptr = cJSON_Print(addtop_obj);
			send_msg(CLOUD_QUEUE, ptr, strlen(ptr));
			free(ptr);
		}
		if (register_cnt) {
			char *ptr = cJSON_Print(register_obj);
			send_msg(CLOUD_QUEUE, ptr, strlen(ptr));
			free(ptr);
		}
		cJSON_Delete(login_obj);
		cJSON_Delete(addtop_obj);
		cJSON_Delete(register_obj);
		return 0;
	}

	int cloud_online_event()
	{
		for (auto it = mp_dev_status.begin(); it != mp_dev_status.end(); it++) {
			it->second.aliyun_status = it->second.aliyun_init_status;
		}
		aly_status = 1;
		return 0;
	}

	int cloud_offline_event()
	{
		for (auto it = mp_dev_status.begin(); it != mp_dev_status.end(); it++) {
			it->second.aliyun_status = it->second.aliyun_init_status;
		}
		aly_status = 0;
		return 0;
	}

	int subdev_login_replay(const cJSON *params)
	{
		int cnt = 0;
		cJSON *devs = cJSON_GetObjectItemCaseSensitive(params, "data");
		cJSON *dev = NULL;
		cJSON *code = cJSON_GetObjectItemCaseSensitive(params, "code");
		if (code && code->valueint != 200) {
			cJSON_ArrayForEach(dev, devs)
			{
				cJSON *icode = cJSON_GetObjectItemCaseSensitive(dev, "code");
				cJSON *addr = cJSON_GetObjectItemCaseSensitive(dev, "deviceName");
				if (cJSON_IsNumber(icode) && icode->valueint == 6204) {
					mp_dev_status[addr->valuestring].aliyun_status = DISABLED;
				}
			}
			return 0;
		}
		cJSON_ArrayForEach(dev, devs)
		{
			cJSON *addr = cJSON_GetObjectItemCaseSensitive(dev, "deviceName");
			cJSON *secret = cJSON_GetObjectItemCaseSensitive(dev, "deviceSecret");
			if (mp_dev_status.find(addr->valuestring) != mp_dev_status.end()) {
				mp_dev_status[addr->valuestring].aliyun_status = LOGIN_READY;
				report_property_byaddr(addr->valuestring);
			}
			cnt++;
		}
		if (cnt == 0) {
			for (auto it = mp_dev_status.begin(); it != mp_dev_status.end(); it++) {
				if (it->second.aliyun_status == TOPO_ADD_READY) {
					it->second.aliyun_status = REGISTER_READY;
				}
			}
		}
		return 0;
	}

	int topo_get_replay(const cJSON *devs)
	{
		int ret = 0;
		cJSON *dev = NULL;
		cJSON_ArrayForEach(dev, devs)
		{
			cJSON *addr = cJSON_GetObjectItemCaseSensitive(dev, "deviceName");
			cJSON *secret = cJSON_GetObjectItemCaseSensitive(dev, "deviceSecret");
			if (cJSON_IsString(addr) && cJSON_IsString(secret) && addr->valuestring != NULL && secret->valuestring != NULL) {
				if (mp_addr_dev.find(addr->valuestring) != mp_addr_dev.end()) {
					if (mp_addr_dev[addr->valuestring]->secret != secret->valuestring) {
						mp_addr_dev[addr->valuestring]->secret = secret->valuestring;
						ret = 1;
					}
				}
				if (mp_dev_status.find(addr->valuestring) != mp_dev_status.end()) {
					mp_dev_status[addr->valuestring].aliyun_init_status = TOPO_ADD_READY;
					mp_dev_status[addr->valuestring].aliyun_status = TOPO_ADD_READY;
				}
			}
		}
		all_subdev_online();
		return ret;
	}

	int topo_add_replay(const cJSON *devs)
	{
		int cnt = 0;
		cJSON *dev = NULL;

		cJSON *login_obj = cJSON_CreateObject();
		cJSON_AddStringToObject(login_obj, "Active", "SUBDEV_LOGIN");
		cJSON *login_array = cJSON_AddArrayToObject(login_obj, "Params");

		cJSON_ArrayForEach(dev, devs)
		{
			cJSON *name = cJSON_GetObjectItemCaseSensitive(dev, "deviceName");
			if (cJSON_IsString(name) && name->valuestring != NULL) {
				if (mp_dev_status.find(name->valuestring) == mp_dev_status.end()) {
					continue;
				}
				mp_dev_status[name->valuestring].aliyun_status = TOPO_ADD_READY;
				cJSON *dev = cJSON_CreateObject();
				cJSON_AddStringToObject(dev, "Name", name->valuestring);
				cJSON_AddStringToObject(dev, "Secret", mp_addr_dev[name->valuestring]->secret.c_str());
				cJSON_AddItemToArray(login_array, dev);
				cnt++;
			}
		}

		if (cnt) {
			char *ptr = cJSON_Print(login_obj);
			send_msg(CLOUD_QUEUE, ptr, strlen(ptr));
			free(ptr);
		} else {
			for (auto it = mp_dev_status.begin(); it != mp_dev_status.end(); it++) {
				if (it->second.aliyun_status == REGISTER_READY) {
					it->second.aliyun_status = NOTHING;
				}
			}
		}
		cJSON_Delete(login_obj);
		return 0;
	}

	int topo_change_notify(const cJSON *params)
	{
		int ret = 0;
		cJSON *data = cJSON_GetObjectItemCaseSensitive(params, "data");
		if (!cJSON_IsObject(data)) {
			return 0;
		}
		cJSON *status = cJSON_GetObjectItemCaseSensitive(data, "status");
		if (!cJSON_IsNumber(status)) {
			return 0;
		}
		if (status->valueint == 0) {
			return 0;
		}
		/*
		0：创建
		1：删除
		2：解除禁用（启用）
		8：禁用
		*/
		int stat = status->valueint;
		cJSON *devs = cJSON_GetObjectItemCaseSensitive(data, "subList");
		cJSON *dev = NULL;
		cJSON_ArrayForEach(dev, devs)
		{
			cJSON *name = cJSON_GetObjectItemCaseSensitive(dev, "deviceName");
			if (mp_addr_dev.find(name->valuestring) == mp_addr_dev.end()) {
				continue;
			}
			if (mp_dev_status[name->valuestring].online) {
				if (stat == 2) {
					mp_dev_status[name->valuestring].aliyun_status = TOPO_ADD_READY;
				}
				if (stat == 8) {
					mp_dev_status[name->valuestring].aliyun_status = DISABLED;
				}
				if (stat == 1) {
					mp_dev_status[name->valuestring].aliyun_status = NOTHING;
				}
				continue;
			}
			if (stat == 1) {
				inair_dev *idev = mp_addr_dev[name->valuestring];
				idev->outair->inair.erase(name->valuestring);

				mp_addr_dev.erase(name->valuestring);
				mp_dev_status.erase(name->valuestring);
				for (auto it = mp_area_dev.begin(); it != mp_area_dev.end(); it++) {
					it->second.erase(name->valuestring);
				}
				for (auto it = mp_saio_dev.begin(); it != mp_saio_dev.end(); it++) {
					it->second.erase(name->valuestring);
				}
				free(idev);
				ret = 1;
			}
		}
		all_subdev_online();
		return ret;
	}

	int sub_register_replay(const cJSON *devs)
	{
		int flag = 0, cnt = 0;
		cJSON *dev = NULL;

		cJSON *addtop_obj = cJSON_CreateObject();
		cJSON_AddStringToObject(addtop_obj, "Active", "SUBDEV_ADD_TOPO");
		cJSON *addtop_array = cJSON_AddArrayToObject(addtop_obj, "Params");
		cJSON_ArrayForEach(dev, devs)
		{
			cJSON *name = cJSON_GetObjectItemCaseSensitive(dev, "deviceName");
			if (mp_addr_dev.find(name->valuestring) == mp_addr_dev.end()) {
				return flag;
			}

			cJSON *secret = cJSON_GetObjectItemCaseSensitive(dev, "deviceSecret");
			if (mp_addr_dev[name->valuestring]->secret != secret->valuestring) {
				mp_addr_dev[name->valuestring]->secret = secret->valuestring;
				flag = 1;
			}

			mp_dev_status[name->valuestring].aliyun_status = REGISTER_READY;
			cJSON *dev = cJSON_CreateObject();
			cJSON_AddStringToObject(dev, "Name", name->valuestring);
			cJSON_AddStringToObject(dev, "Secret", secret->valuestring);
			cJSON_AddItemToArray(addtop_array, dev);
			cnt++;
		}

		if (cnt) {
			char *ptr = cJSON_Print(addtop_obj);
			send_msg(CLOUD_QUEUE, ptr, strlen(ptr));
			free(ptr);
		}
		cJSON_Delete(addtop_obj);
		return flag;
	}

	int check_post_replay()
	{
		for (auto it = mp_post_id.begin(); it != mp_post_id.end(); it++) {
			if (it->second.second++ > 5) {
				report_property_byaddr(it->first.c_str());
			}
		}
		return 0;
	}
	int property_post_replay(const cJSON *params)
	{
		cJSON *addr = cJSON_GetObjectItemCaseSensitive(params, "Addr");
		cJSON *id = cJSON_GetObjectItemCaseSensitive(params, "Id");
		if (!cJSON_IsString(addr) || addr->valuestring == NULL || !cJSON_IsString(id) || id->valuestring == NULL) {
			return -1;
		}
		if (mp_addr_dev.find(addr->valuestring) == mp_addr_dev.end()) {
			return -1;
		}
		uint32_t pid = strtoul(id->valuestring, NULL, 10);
		if (pid == mp_post_id[addr->valuestring].first) {
			mp_post_id.erase(addr->valuestring);
			return 1;
		}

		return 1;
	}
};
